//package com.boot2.core.dao.datasource.xa;
//
//import com.alibaba.druid.pool.xa.DruidXADataSource;
//import com.atomikos.jdbc.AtomikosDataSourceBean;
//import com.boot2.core.dao.datasource.DynamicDataSource;
//import com.boot2.core.dao.datasource.DynamicDataSourceInitializer;
//import lombok.Data;
//import org.apache.ibatis.mapping.DatabaseIdProvider;
//import org.apache.ibatis.plugin.Interceptor;
//import org.apache.ibatis.session.Configuration;
//import org.apache.ibatis.session.ExecutorType;
//import org.apache.ibatis.session.SqlSessionFactory;
//import org.mybatis.spring.SqlSessionFactoryBean;
//import org.mybatis.spring.SqlSessionTemplate;
//import org.mybatis.spring.annotation.MapperScan;
//import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
//import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
//import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
//import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
//import org.mybatis.spring.mapper.MapperFactoryBean;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.beans.factory.InitializingBean;
//import org.springframework.beans.factory.ObjectProvider;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
//import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.boot.context.properties.EnableConfigurationProperties;
//import org.springframework.boot.context.properties.bind.Bindable;
//import org.springframework.boot.context.properties.bind.Binder;
//import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
//import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
//import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
//import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
//import org.springframework.context.EnvironmentAware;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Primary;
//import org.springframework.core.env.Environment;
//import org.springframework.core.io.Resource;
//import org.springframework.core.io.ResourceLoader;
//import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
//import org.springframework.util.*;
//
//import javax.sql.DataSource;
//import javax.sql.XADataSource;
//import java.io.IOException;
//import java.lang.reflect.Field;
//import java.util.*;
//
///**
// * 启用分布式支持说明 ：<br>
// * <p>
// * 需使用MapperScan注解并指定factoryBean为{@link XaDynamicMapperFactoryBean}和扫描的basePackages信息.<br/>
// * 无MapperScan默认采用{@link MybatisAutoConfiguration.MapperScannerRegistrarNotFoundConfiguration}配置,
// * {@link MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar}扫描Mapper，
// * factoryBean默认采用MapperFactoryBean无法实现Mapper映射到所有数据源<br/>
// * e.g：<br>
// * <p>
// * {@link MapperScan @MapperScan}(factoryBean = XaDynamicMapperFactoryBean.class, basePackages = {"com.abc.mapper"})<br>
// * {@link org.springframework.boot.autoconfigure.SpringBootApplication @SpringBootApplication}<br>
// * class ApplicationDemo {
// * ..
// * }
// * </p>
// * </p>
// *
// * @author ZHUFEIFEI
// * @see MybatisAutoConfiguration
// * @see org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration
// */
////@Import({XaDynamicDataSourceRegistrar.class})
//@ConfigurationProperties(prefix = "spring")
//@ConditionalOnClass({AtomikosDataSourceBean.class, MapperFactoryBean.class})
////@ConditionalOnProperty(prefix = "spring.jta", name = "enabled", matchIfMissing = true)
//@EnableConfigurationProperties({MybatisProperties.class})
//@org.springframework.context.annotation.Configuration
//@Data
//public class XaDynamicDataSourceConfiguration implements InitializingBean, EnvironmentAware, DynamicDataSourceInitializer {
//
//    /**
//     * 配置别名设置
//     */
//    private final static ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
////    @Autowired(required = false)
////    AbstractRoutingDataSource dataSource;
////
////    @Autowired(required = false)
////    DynamicDataSource dynamicDataSource;
//
//    static {
//        //其他数据源可能不叫url, 所以设置别名匹配
//        aliases.addAliases("url", new String[]{"jdbc-url"});
//        aliases.addAliases("username", new String[]{"user"});
//    }
//
//    private final Logger log = LoggerFactory.getLogger(getClass());
//    private Environment environment;
//    private MybatisProperties properties;
//    private Interceptor[] interceptors;
//    private ResourceLoader resourceLoader;
//    private DatabaseIdProvider databaseIdProvider;
//    private List<ConfigurationCustomizer> configurationCustomizers;
//    /**
//     * 参数绑定工具 springboot2.0新推出
//     */
//    private Binder binder;
//    /**
//     * 原始的多个数据源集合，比如test1为key
//     */
//    private Map<String, DruidXADataSource> customDatasource;
//
////    @Override
////    public void setEnvironment(Environment environment) {
////        this.environment = environment;
////    }
//    /**
//     * 构建出来的分布式数据源集合
//     */
//    private Map<String, DataSource> xaDataSources = new HashMap<>();
//    /**
//     * 默认的数据源名称
//     */
//    @Value("${spring.datasource.customDefaultName}")
//    private String customDefaultDatasourceName;
//
//    public XaDynamicDataSourceConfiguration(MybatisProperties properties,
//                                            ObjectProvider<Interceptor[]> interceptorsProvider,
//                                            ResourceLoader resourceLoader,
//                                            ObjectProvider<DatabaseIdProvider> databaseIdProvider,
//                                            ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
//        this.properties = properties;
//        this.interceptors = interceptorsProvider.getIfAvailable();
//        this.resourceLoader = resourceLoader;
//        this.databaseIdProvider = databaseIdProvider.getIfAvailable();
//        this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
//    }
//
//    @Bean
//    public SqlSessionFactory sqlSessionFactory() throws Exception {
////        System.out.println("dynamicDataSource = " + dynamicDataSource);
//        DynamicDataSource dynamicDataSource = dynamicDataSource();
//        XaDynamicSqlSessionFactory sqlSessionFactory = new XaDynamicSqlSessionFactory();
//        Map<String, DataSource> dataSources = extractDataSource(dynamicDataSource);
//        Assert.isTrue(!dataSources.isEmpty(), "AbstractRoutingDataSource targetDataSources can not be empty!");
//        Binder binder = Binder.get(this.environment);
////        Map configProperties = binder.bind("mybatis.configuration", Bindable.of(Map.class)).orElseGet(() -> new HashMap(1));
//        Map<String, Object> configProperties = binder.bind("mybatis", Bindable.of(Map.class)).orElseGet(() -> new HashMap<>(1));
//        for (Map.Entry<String, DataSource> kv : dataSources.entrySet()) {
//            sqlSessionFactory.add(kv.getKey(), buildSqlSessionFactory(kv.getKey(), kv.getValue(), configProperties));
//            log.debug("Create SqlSessionFactory : {}", kv.getKey());
//        }
//        return sqlSessionFactory;
//    }
//
//    @Bean
////    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
//    public SqlSessionTemplate sqlSessionTemplate() throws Exception {
//        SqlSessionFactory sqlSessionFactory = sqlSessionFactory();
//        ExecutorType executorType = this.properties.getExecutorType();
////        Assert.isTrue(sqlSessionFactory.getClass().isAssignableFrom(XaDynamicSqlSessionFactory.class), "Not support SqlSessionFactory type!");
//        XaDynamicSqlSessionFactory factory = XaDynamicSqlSessionFactory.class.cast(sqlSessionFactory);
//        XaDynamicSqlSessionTemplate template = new XaDynamicSqlSessionTemplate(sqlSessionFactory);
//        for (Map.Entry<String, SqlSessionFactory> kv : factory.sqlSessionFactories().entrySet()) {
//            if (executorType != null) {
//                template.add(kv.getKey(), new SqlSessionTemplate(kv.getValue(), executorType));
//            } else {
//                template.add(kv.getKey(), new SqlSessionTemplate(kv.getValue()));
//            }
//            log.debug("Create SqlSessionTemplate : {}", kv.getKey());
//        }
//        return template;
//    }
//
//    //===数据源本身============
//
//    @Override
//    public void afterPropertiesSet() {
//        checkConfigFileExists();
//    }
//
//    private void checkConfigFileExists() {
//        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
//            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
//            Assert.state(resource.exists(), "Cannot find config location: " + resource
//                    + " (please add config file or check your Mybatis configuration)");
//        }
//    }
//
//
////    private Environment environment;
//
//    private Map<String, DataSource> extractDataSource(AbstractRoutingDataSource dataSource) {
//        Field f = ReflectionUtils.findField(dataSource.getClass(), "targetDataSources");
//        f.setAccessible(true);
//        return (Map<String, DataSource>) ReflectionUtils.getField(f, dataSource);
//    }
//
//    private SqlSessionFactory buildSqlSessionFactory(String key, DataSource dataSource, Map<String, Object> configProperties) throws Exception {
//        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
//        //
//        factory.setEnvironment(SqlSessionFactoryBean.class.getSimpleName() + "#" + key);
//        factory.setDataSource(dataSource);
//        factory.setVfs(SpringBootVFS.class);
//        if (StringUtils.hasText(this.properties.getConfigLocation())) {
//            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
//        }
//        applyConfiguration(factory, configProperties);
//        if (this.properties.getConfigurationProperties() != null) {
//            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
//        }
//        if (!ObjectUtils.isEmpty(this.interceptors)) {
//            factory.setPlugins(this.interceptors);
//        }
//        if (this.databaseIdProvider != null) {
//            factory.setDatabaseIdProvider(this.databaseIdProvider);
//        }
//        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
//            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
//        }
//        if (this.properties.getTypeAliasesSuperType() != null) {
//            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
//        }
//        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
//            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
//        }
//        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
//            factory.setMapperLocations(this.properties.resolveMapperLocations());
//        }
//        return factory.getObject();
//    }
//
//    private void applyConfiguration(SqlSessionFactoryBean factory, Map<String, Object> configProperties) throws IOException, ClassNotFoundException {
//        //否则多个sqlSessionFactory会共用一个，导致dataSource最后一个覆盖之前所有的
//        Configuration configuration;
//        if (configProperties.isEmpty()) {
//            configuration = new Configuration();
//        } else {
//            configuration = this.bind(Configuration.class, configProperties);
//        }
//        if (!CollectionUtils.isEmpty(this.configurationCustomizers)) {
//            for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
//                customizer.customize(configuration);
//            }
//        }
//        factory.setConfiguration(configuration);
//    }
////    private Map<String, DruidDataSource> customDatasource;
//
//    private <T> T bind(Class<T> clazz, Map<String, Object> properties) {
//        ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
//        Binder binder = new Binder(new ConfigurationPropertySource[]{source});
//        return binder.bind(ConfigurationPropertyName.EMPTY, Bindable.of(clazz)).get();
//    }
//
//    @Override
//    public void setEnvironment(Environment environment) {
//        this.environment = environment;
//        //获取参数绑定器，用于将参数与类属性进行绑定，就是赋值
//        this.binder = Binder.get(environment);
//    }
//
//    /**
//     * 获取默认的数据源
//     *
//     * @return
//     */
//    public javax.sql.DataSource getDefaultDataSource() {
//        return getDataSource(customDefaultDatasourceName);
//    }
//
//    /**
//     * 根据配置的数据源名称获取数据源
//     *
//     * @param name
//     * @return
//     */
//    public javax.sql.DataSource getDataSource(String name) {
//        Assert.notNull(xaDataSources, "没有配置自定义分布式数据源，不需要获取动态分布式数据源");
//        javax.sql.DataSource dataSource = xaDataSources.get(name);
//        Assert.notNull(dataSource, "配置了分布式数据源名称【" + customDefaultDatasourceName + "】，但是没有找到该分布式数据源，请检查配置");
//        return dataSource;
//    }
//
//    /**
//     * 创建动态数据源,动态数据源应标为@Primary,当未指定数据源时，整个系统都使用此数据源
//     *
//     * @return
//     */
////    @Bean(name = "dynamicDataSource")
//    @Primary
//    public DynamicDataSource dynamicDataSource() {
//        Map<String, Object> xaProperties = this.binder.bind("spring.jta.atomikos.datasource", Bindable.of(Map.class)).orElseGet(() -> new HashMap<>(1));
//        Set<Map.Entry<String, DruidXADataSource>> entrySet = customDatasource.entrySet();
//        Iterator<Map.Entry<String, DruidXADataSource>> iterator = entrySet.iterator();
//        while (iterator.hasNext()) {
//            Map.Entry<String, DruidXADataSource> entry = iterator.next();
//            javax.sql.DataSource dataSource = wrap(entry.getValue(), entry.getKey(), xaProperties);
//            xaDataSources.put(entry.getKey(), dataSource);
//            log.info("注册分布式数据源 xaDataSources: " + xaDataSources);
//        }
//
//        Map<Object, Object> targetDataSources = new HashMap<>();
////        targetDataSources.putAll(xaDataSources);
//        targetDataSources.putAll(customDatasource);
//        DynamicDataSource dataSource = new DynamicDataSource();
//        // 该方法是AbstractRoutingDataSource的方法
//        dataSource.setTargetDataSources(targetDataSources);
////        dataSource.setTargetDataSources(dataSources);
////        // 默认的dataSource
//        dataSource.setDefaultTargetDataSource(getDefaultDataSource());
//        dataSource.afterPropertiesSet();
//        return dataSource;
//    }
//
//    private DataSource wrap(XADataSource xaDataSource, String name, Map<String, Object> xaProperties) {
//        org.springframework.boot.jta.atomikos.AtomikosDataSourceBean bean = new org.springframework.boot.jta.atomikos.AtomikosDataSourceBean();
//        if (!xaProperties.isEmpty()) {
//            this.bind(bean, xaProperties);
//        }
//        bean.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
//        bean.setUniqueResourceName(name);
//        bean.setXaDataSource(xaDataSource);
//        return bean;
//    }
//
//    private <T> T bind(T bean, Map<String, Object> properties) {
//        ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
//        Binder binder = new Binder(new ConfigurationPropertySource[]{source.withAliases(aliases)});
//        return binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(bean)).get();
//    }
//
//}
